/**
 * 
 */
package com.dalonedrow.module.hq.systems;

import com.dalonedrow.engine.sprite.base.SimpleVector3;
import com.dalonedrow.engine.systems.JOGLErrorHandler;
import com.dalonedrow.engine.systems.base.Interactive;
import com.dalonedrow.globalvariables.constants.hq.GlobalConstants;
import com.dalonedrow.module.hq.rpg.HQCharacter;
import com.dalonedrow.module.hq.rpg.HQInteractiveObject;
import com.dalonedrow.pooled.PooledStringBuilder;
import com.dalonedrow.pooled.StringBuilderPool;
import com.dalonedrow.rpg.base.flyweights.Spell;
import com.dalonedrow.rpg.base.systems.Spells;

/**
 * @author Donald
 */
public final class HQSpells implements Spells<HQInteractiveObject, Spell> {
	/** the singeton instance of {@link HQSpells}. */
	private static HQSpells	instance;
	/**
	 * Gets the one and only instance of {@link HQSpells}.
	 * @return {@link HQSpells}
	 */
	public static HQSpells getInstance() {
		if (HQSpells.instance == null) {
			HQSpells.instance = new HQSpells();
		}
		return HQSpells.instance;
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public float applyColdProtection(HQInteractiveObject io, float damages) {
		// TODO Auto-generated method stub
		return 0;
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public float applyFireProtection(HQInteractiveObject io, float damages) {
		// TODO Auto-generated method stub
		return 0;
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void cancelSpellTarget() {
		// TODO Auto-generated method stub
		
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void clearParticles() {
		// TODO Auto-generated method stub
		
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void initKnownSpells() {
		// TODO Auto-generated method stub
		
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void kill(int i) {
		// TODO Auto-generated method stub
		
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void launchSpellTarget(HQInteractiveObject io) {
		// TODO Auto-generated method stub
		
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean noMagicAllowed() {
		// TODO Auto-generated method stub
		return false;
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void spellCastNotify(long num) {
		// TODO Auto-generated method stub
		
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void spellCastNotifyOnlyTarget(long num) {
		// TODO Auto-generated method stub
		
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void spellEndNotify(long num) {
		// TODO Auto-generated method stub
		
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void tryToCastSpell(HQInteractiveObject io, int spellid, int level,
			int target, int flags, long duration) {
		// TODO Auto-generated method stub
		
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void update() {
		// TODO Auto-generated method stub
		
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void updateLightning() {
		// TODO Auto-generated method stub
		
	}
	/** the list of spells, active and inactive. */
	private Spell[]	spells	= new Spell[GlobalConstants.MAX_SPELLS];
	/**
	 * {@inheritDoc}
	 */
	@Override
	public Spell getSpell(final int spellId) {
		return spells[spellId];
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void addSpellOn(final int caster, final int spell) throws Exception {
		if (Interactive.getInstance().hasIO(caster)
				&& spell > 0) {
			HQInteractiveObject io =
					(HQInteractiveObject) Interactive.getInstance().getIO(
							caster);
			io.addSpellOn(spell);
		}
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean canPayMana(final int num, final float cost,
			final boolean playSound) throws Exception {
		boolean canPay = false;
		if (num >= 0) {
			if (spells[num].hasFlag(GlobalConstants.SPELLCAST_FLAG_NOMANA)) {
				canPay = true;
			} else {
				int casterId = spells[num].getCaster();
				if (Interactive.getInstance().hasIO(casterId)) {
					HQInteractiveObject caster =
							(HQInteractiveObject) Interactive.getInstance()
									.getIO(casterId);
					if (caster.hasIOFlag(GlobalConstants.IO_01_PC)) {
						canPay = true;
					} else if (caster.hasIOFlag(GlobalConstants.IO_03_NPC)) {
						canPay = true;
					}
				}
			}
		}
		return canPay;
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getSpellId(final char[] spell) {
		int id = -1;
		PooledStringBuilder sb =
				StringBuilderPool.getInstance().getStringBuilder();
		sb.append(spell);
		id = this.getSpellId(sb.toString());
		sb.returnToPool();
		return id;
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getSpellId(final String spell) {
		int id = -1;
		if (spell.equalsIgnoreCase("BALL_OF_FLAME")) {
			id = GlobalConstants.SPELL_10_FIRE_BALL_OF_FLAME;
		} else if (spell.equalsIgnoreCase("FIRE_OF_WRATH")) {
			id = GlobalConstants.SPELL_02_FIRE_FIRE_OF_WRATH;
		} else if (spell.equalsIgnoreCase("COURAGE")) {
			id = GlobalConstants.SPELL_06_FIRE_COURAGE;
		} else if (spell.equalsIgnoreCase("HEAL_BODY")) {
			id = GlobalConstants.SPELL_01_EARTH_HEAL_BODY;
		} else if (spell.equalsIgnoreCase("ROCK_SKIN")) {
			id = GlobalConstants.SPELL_05_EARTH_ROCK_SKIN;
		} else if (spell.equalsIgnoreCase("PASS_THROUGH_ROCK")) {
			id = GlobalConstants.SPELL_09_EARTH_PASS_THROUGH_ROCK;
		} else if (spell.equalsIgnoreCase("GENIE")) {
			id = GlobalConstants.SPELL_08_AIR_GENIE;
		} else if (spell.equalsIgnoreCase("TEMPEST")) {
			id = GlobalConstants.SPELL_04_AIR_TEMPEST;
		} else if (spell.equalsIgnoreCase("SWIFT_WIND")) {
			id = GlobalConstants.SPELL_00_AIR_SWIFT_WIND;
		} else if (spell.equalsIgnoreCase("SLEEP")) {
			id = GlobalConstants.SPELL_07_WATER_SLEEP;
		} else if (spell.equalsIgnoreCase("WATER_OF_HEALING")) {
			id = GlobalConstants.SPELL_03_WATER_WATER_OF_HEALING;
		} else if (spell.equalsIgnoreCase("VEIL_OF_MIST")) {
			id = GlobalConstants.SPELL_11_WATER_VEIL_OF_MIST;
		}
		return id;
	}
	/**
	 * Determines if a specific IO has a spell on it.
	 * @param ioid the IO's reference id
	 * @param spellType the type of spell
	 * @return true if the IO has the spell in effect; false otherwise
	 * @throws Exception if the IO doesn't exist
	 */
	public boolean hasSpellOn(final int ioid, final int spellType) 
	throws Exception {
		if (!Interactive.getInstance().hasIO(ioid)) {
			PooledStringBuilder sb =
					StringBuilderPool.getInstance().getStringBuilder();
			sb.append("IO ref id ");
			sb.append(ioid);
			sb.append(" does not exist.");
			Exception ex = new Exception(sb.toString());
			sb.returnToPool();
			throw ex;
		}
		if (Interactive.getInstance().getIO(ioid) == null) {
			PooledStringBuilder sb =
					StringBuilderPool.getInstance().getStringBuilder();
			sb.append("IO ref id ");
			sb.append(ioid);
			sb.append(" does not exist.");
			Exception ex = new Exception(sb.toString());
			sb.returnToPool();
			throw ex;
		}
		boolean has = false;
		HQInteractiveObject io = 
			(HQInteractiveObject) Interactive.getInstance().getIO(ioid);
		for (int i = 0; i < io.getNumberOfSpellsOn(); i++) {
			Spell spell = getSpell(io.getSpellOn(i));
			if (spell.getType() == spellType) {
				has = true;
				break;
			}
		}
		return has;
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getSpellOn(final HQInteractiveObject io,
			final int spellType) {
		int id = -1;
		if (io != null) {
			for (int i = 0; i < io.getNumberOfSpellsOn(); i++) {
				if (spells[io.getSpellOn(i)].getType() == spellType
						&& spells[io.getSpellOn(i)].exists()) {
					id = io.getSpellOn(i);
				}
			}
		}
		return id;
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public String makeSpellName(final int type) throws Exception {
		String name = "";
		switch (type) {
		case GlobalConstants.SPELL_10_FIRE_BALL_OF_FLAME:
			name = "BALL_OF_FLAME";
			break;
		case GlobalConstants.SPELL_02_FIRE_FIRE_OF_WRATH:
			name = "FIRE_OF_WRATH";
			break;
		case GlobalConstants.SPELL_06_FIRE_COURAGE:
			name = "COURAGE";
			break;
		case GlobalConstants.SPELL_01_EARTH_HEAL_BODY:
			name = "HEAL_BODY";
			break;
		case GlobalConstants.SPELL_05_EARTH_ROCK_SKIN:
			name = "ROCK_SKIN";
			break;
		case GlobalConstants.SPELL_09_EARTH_PASS_THROUGH_ROCK:
			name = "PASS_THROUGH_ROCK";
			break;
		case GlobalConstants.SPELL_08_AIR_GENIE:
			name = "GENIE";
			break;
		case GlobalConstants.SPELL_04_AIR_TEMPEST:
			name = "TEMPEST";
			break;
		case GlobalConstants.SPELL_00_AIR_SWIFT_WIND:
			name = "SWIFT_WIND";
			break;
		case GlobalConstants.SPELL_07_WATER_SLEEP:
			name = "SLEEP";
			break;
		case GlobalConstants.SPELL_03_WATER_WATER_OF_HEALING:
			name = "WATER_OF_HEALING";
			break;
		case GlobalConstants.SPELL_11_WATER_VEIL_OF_MIST:
			name = "VEIL_OF_MIST";
			break;
		default:
			PooledStringBuilder sb =
					StringBuilderPool.getInstance().getStringBuilder();
			sb.append("Invalid spell type - ");
			sb.append(type);
			Exception ex = new Exception(sb.toString());
			sb.returnToPool();
			throw ex;
		}
		return name;
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void manageMagic(final HQInteractiveObject io) {
		if (io != null) {
			if (io.hasIOFlag(GlobalConstants.IO_01_PC)) {
				// check to see if PC is in combat
				// check to make sure controls aren't blocked
				// and PC isn't paralyzed

				HQCharacter pc = io.getPCData();
				if (HQPlayer.getInstance().isInFightMode(pc)) {
					// change player's animation to magic, and set
					// next animation to fighting stance
				}
				// if player is paralyzed, or unable to perform magic
				// remove magic animations
			}
		}
	}
	/**
	 * Notifies only the target of a spell that the spell has been cast.
	 * @param num the spell id.
	 * @throws Exception if an error occurs
	 */
	private void notifyOnlyTargetSpellCast(final int num) throws Exception {
		if (num >= 0
				&& num < GlobalConstants.MAX_SPELLS
				&& spells[num].getTarget() >= 0
				&& Interactive.getInstance().hasIO(spells[num].getTarget())) {
			String spell = makeSpellName(spells[num].getType());
			int source = spells[num].getCaster();
			if (spell != null) {
				HQInteractiveObject ioSource = null;
				if (Interactive.getInstance().hasIO(source)) {
					ioSource =
							(HQInteractiveObject) Interactive
									.getInstance().getIO(source);
				}
				HQScript.getInstance().setEventSender(ioSource);
				String param = String.format("%s %f", spell,
						spells[num].getCasterLevel());
				// notify target that caster cast a spell
				HQScript.getInstance().sendIOScriptEvent(
						(HQInteractiveObject) Interactive.getInstance().getIO(
								spells[num].getTarget()),
						GlobalConstants.SM_42_SPELLCAST,
						new Object[] {spell,  spells[num].getCasterLevel()},
						null
						);
			}
		}
	}
	/**
	 * Notifies the all interactive objects that a spell has been cast.
	 * @param num the spell id.
	 * @throws Exception if an error occurs
	 */
	private void notifySpellCast(final int num) throws Exception {
		if (num >= 0
				&& num < GlobalConstants.MAX_SPELLS) {
			String spell = makeSpellName(spells[num].getType());
			int source = spells[num].getCaster();
			if (spell != null) {
				int ioNum = Interactive.getInstance().getNumberIOs();
				for (int i = 0; i < ioNum; i++) {
					if (Interactive.getInstance().hasIO(i)) {
						HQInteractiveObject io =
								(HQInteractiveObject) Interactive.getInstance()
										.getIO(i);
						HQInteractiveObject ioSource = null;
						if (Interactive.getInstance().hasIO(source)) {
							ioSource =
									(HQInteractiveObject) Interactive
											.getInstance().getIO(source);
						}
						HQScript.getInstance().setEventSender(ioSource);
						String param = String.format("%s %f", spell,
								spells[num].getCasterLevel());
						// notify all IOs that caster cast a spell
						HQScript.getInstance().sendIOScriptEvent(
								io,
								GlobalConstants.SM_42_SPELLCAST,
								new Object[] { spell,
										spells[num].getCasterLevel() },
								null
								);
					}
				}
			}
		}
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void analyze() {
		// NOT USED
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void init() {
		for (int i = 0; i < GlobalConstants.MAX_SPELLS; i++) {
			spells[i].setTimeToLive(0);
			spells[i].setTurnsToLive(0);
			spells[i].setExists(false);
		}
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void cancelAll() throws Exception {
		for (int i = 0; i < GlobalConstants.MAX_SPELLS; i++) {
			if (spells[i].exists()) {
				spells[i].setTimeToLive(0);
				spells[i].setTurnsToLive(0);		
			}
		}
		update();
		// remove all spells on the players
		for (int i = 0; i < Interactive.getInstance().getNumberIOs(); i++) {
			if (Interactive.getInstance().hasIO(i)) {
				HQInteractiveObject io =
						(HQInteractiveObject) Interactive.getInstance()
								.getIO(i);
				if (io.hasIOFlag(GlobalConstants.IO_01_PC)) {
					removeAllSpellsOn(io);
				}
			}
		}
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void clearAll() throws Exception {
		for (int i = 0; i < GlobalConstants.MAX_SPELLS; i++) {
			if (spells[i].exists()) {
				spells[i].setTimeToLive(0);
				spells[i].setTurnsToLive(0);
				spells[i].setExists(false);
			}
		}
		for (int i = 0; i < Interactive.getInstance().getNumberIOs(); i++) {
			if (Interactive.getInstance().hasIO(i)) {
				HQInteractiveObject io =
						(HQInteractiveObject) Interactive.getInstance()
								.getIO(i);
					removeAllSpellsOn(io);
			}
		}
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getFree() {
		int index = -1;
		for (int i = 0; i < GlobalConstants.MAX_SPELLS; i++) {
			if (!spells[i].exists()) {
				index = i;
			}
		}
		return index;
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean existAnyInstance(final int typ) {
		boolean exists = false;
		for (int i = 0; i < GlobalConstants.MAX_SPELLS; i++) {
			if (spells[i].exists() 
					&& spells[i].getType() == typ) {
				exists = true;
				break;
			}
		}
		return exists;
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public long getInstance(final int typ) {
		int index = -1;
		for (int i = 0; i < GlobalConstants.MAX_SPELLS; i++) {
			if (spells[i].exists() 
					&& spells[i].getType() == typ) {
				index = i;
				break;
			}
		}
		return index;
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public long getInstanceForThisCaster(final int typ, final int caster) {
		int index = -1;
		for (int i = 0; i < GlobalConstants.MAX_SPELLS; i++) {
			if (spells[i].exists() 
					&& spells[i].getType() == typ
					&& spells[i].getCaster() == caster) {
				index = i;
				break;
			}
		}
		return index;
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean existAnyInstanceForThisCaster(final int typ, 
			final int caster) {
		boolean exists = false;
		for (int i = 0; i < GlobalConstants.MAX_SPELLS; i++) {
			if (spells[i].exists() 
					&& spells[i].getType() == typ
					&& spells[i].getCaster() == caster) {
				exists = true;
				break;
			}
		}
		return exists;
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void abortSpellSound() {
		// NOT USED
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void fizzle(final int num) {
		if (num < 0) {
			//ARX_SOUND_PlaySFX(SND_MAGIC_FIZZLE); // player fizzle
		} else {
			spells[num].setTimeToLive(0);
			spells[num].setTurnsToLive(0);
			// play a sound at casters position
			/*
			if (spells[num].caster >= 0)
				ARX_SOUND_PlaySFX(SND_MAGIC_FIZZLE, &spells[num].caster_pos);
				*/
		}
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void fizzleAllSpellsFromCaster(final int casterId) {
		for (int i = 0; i < GlobalConstants.MAX_SPELLS; i++) {
			if (spells[i].exists()
				&& spells[i].getCaster() == casterId) {
				spells[i].setTimeToLive(0);
				spells[i].setTurnsToLive(0);
			}
		}
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void fizzleNoMana(final int num) {
		// NOT USED
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void precastReset() {
		// NOT USED
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void precastAdd(final long typ, final long level, final long flags, 
			final long duration) {
		// NOT USED
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public long precastCheckCanPayMana(final long num, final float cost, 
			final boolean playSound) {
		// NOT USED
		return 0;
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void precastCheck() {
		// NOT USED
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void precastLaunch(final long num) {
		// NOT USED
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public float getManaCost(long _lNumSpell, long lNumSpellTab) {
		// NOT USED
		return 0;
	}
	private HQInteractiveObject EVENT_SENDER;
	private void notify(final int num) {
		try {
		if (num < 0) return;

		if (num >= GlobalConstants.MAX_SPELLS) return;

		String spell = makeSpellName(spells[num].getType());
		int source = spells[num].getCaster();

		if (spell != null) {
			for (int i = 0; i < Interactive.getInstance().getNumberIOs(); i++) {
				if (DQInteractive.getInstance().hasIO(i)) {
					if (source >= 0) {
						EVENT_SENDER = (HQInteractiveObject) DQInteractive.getInstance().getIO(source);
					} else {
						EVENT_SENDER = null;
					}
					HQScript.getInstance().sendIOScriptEvent(
							(HQInteractiveObject) DQInteractive.getInstance().getIO(i), 
							GlobalConstants.SM_42_SPELLCAST,
							new Object[] { spell,
								spells[num].getCasterLevel() }, 
							null);
				}
			}	
		}
		} catch (Exception ex) {
			JOGLErrorHandler.getInstance().fatalError(ex);
		}
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public long launchSpell(final int typ, final int source, final int flagss, 
			final int levell, final int target, final long timeDuration, 
			final int turnDuration) {
		return 0;
	}
	/**
	 * Notifies the all interactive objects that a spell has ended.
	 * @param num the spell id.
	 * @throws Exception if an error occurs
	 */
	private void notifySpellEnd(final int num) throws Exception {
		if (num >= 0
				&& num < GlobalConstants.MAX_SPELLS) {
			String spell = makeSpellName(spells[num].getType());
			if (spell != null) {
				int source = spells[num].getCaster();

				if (spells[num].getType() == GlobalConstants.SPELL_07_WATER_SLEEP) {
					HQInteractiveObject ioSource = null;
					if (Interactive.getInstance().hasIO(source)) {
						ioSource =
								(HQInteractiveObject) Interactive.getInstance()
										.getIO(
												source);
					}
					HQScript.getInstance().setEventSender(ioSource);
					if (Interactive.getInstance()
							.hasIO(spells[num].getTarget())) {
						HQInteractiveObject ioTarget =
								(HQInteractiveObject) Interactive.getInstance()
										.getIO(
												spells[num].getTarget());
						String param = String.format("%s %f", spell,
								spells[num].getCasterLevel());
						HQScript.getInstance().sendIOScriptEvent(
								ioTarget,
								GlobalConstants.SM_48_SPELLEND,
								new Object[] { spell,
										spells[num].getCasterLevel() },
								null
								);
					}
				} else {
					int ioNum = Interactive.getInstance().getNumberIOs();
					for (int i = 0; i < ioNum; i++) {
						if (Interactive.getInstance().hasIO(i)) {
							HQInteractiveObject io =
									(HQInteractiveObject) Interactive
											.getInstance()
											.getIO(i);
							HQInteractiveObject ioSource = null;
							if (Interactive.getInstance().hasIO(source)) {
								ioSource =
										(HQInteractiveObject) Interactive
												.getInstance().getIO(source);
							}
							HQScript.getInstance().setEventSender(ioSource);
							String param = String.format("%s %f", spell,
									spells[num].getCasterLevel());
							// notify all IOs that spell ended
							HQScript.getInstance().sendIOScriptEvent(
									io,
									GlobalConstants.SM_48_SPELLEND,
									new Object[] { spell,
											spells[num].getCasterLevel() },
									null
									);
						}
					}
				}
			}
		}
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void removeAllSpellsOn(final HQInteractiveObject io) {
		io.removeAllSpells();
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void removeMultiSpellOn(final int spell) throws Exception {
		for (int i = 0; i < Interactive.getInstance().getNumberIOs(); i++) {
			if (Interactive.getInstance().hasIO(i)) {
				removeSpellOn(i, spells[spell].getType());
			}
		}
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void removeSpellOn(final int caster, final int spell)
			throws Exception {
		if (Interactive.getInstance().hasIO(caster)
				&& spell > 0) {
			HQInteractiveObject io =
					(HQInteractiveObject) Interactive.getInstance().getIO(
							caster);
			io.removeSpellOn(spell);
		}
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void resetRecognition() {
		// NOT USED
	}
	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean TemporaryGetSpellTarget(SimpleVector3 from) {
		// NOT USED
		return false;
	}
}
